package core

import (
    "net/http"
    "reflect"
    "strings"
    "gougouapi/core/render"
    "fmt"
    "time"
    "gougouapi/core/logger"
)

const (
    ReplyTypeHTML = 1 + iota
    ReplyTypeJSON
)

var (
    onlyHttp *Http
)

type HttpConf struct {
    Addr                string
    HTMLRender          render.HtmlRender
    JsonRender          render.JsonRender
    DefaultReplyType    int
}

type HttpReply struct {
    ReplyType       int
    HeaderCode      int
    Data            interface{}
    TemplateName    string
}

type Http struct {
    HandlerMap      map[string]reflect.Type
    Conf            *HttpConf
}

func (this *Http) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    startTime := time.Now()
    defer func(startTime time.Time) {
        dis := time.Now().Sub(startTime).String()
        logger.Time.Println(r.Method, r.RequestURI, dis)
    }(startTime)
    requestPath := r.URL.Path
    navigationAry, params := this.getNavigation(requestPath)
    if len(navigationAry) < 2 {
        this.NotFound(w, r)
        return
    }
    handerName := navigationAry[0]
    methodName := navigationAry[1]
    this.run(handerName, methodName, params, w, r)
}

func (this *Http) NotFound(w http.ResponseWriter, r *http.Request) {
    http.NotFound(w, r)
}

func (this *Http) getNavigation(requestPath string) ([]string, map[string]string) {
    requestPath = strings.Trim(requestPath, "/")
    ary := strings.Split(requestPath, "/")
    return ary, map[string]string{}
}

func (this *Http) run(handlerName, methodName string, params map[string]string, w http.ResponseWriter, r *http.Request) {
    typ, ok := this.HandlerMap[handlerName]
    if !ok {
        this.NotFound(w, r)
        return
    }
    if typ.Kind() == reflect.Ptr {
        typ = typ.Elem()
    }
    vof := reflect.New(typ)
    funVal := vof.MethodByName(r.Method + "_" + methodName)
    if !funVal.IsValid() {
        funVal = vof.MethodByName("Any_" + methodName)
    }
    if !funVal.IsValid() {
        this.NotFound(w, r)
        return
    }
    handler := vof.Interface().(HttpHandlerInterface)
    handler.SetWriter(w)
    handler.SetRequest(r)
    handler.SetPathParams(params)
    reply := &HttpReply{}
    reply.ReplyType = this.Conf.DefaultReplyType
    reply.TemplateName = fmt.Sprintf("%s.%s.html", handlerName, methodName)
    reply.HeaderCode = 200
    if err := handler.PreAction(); err != nil {
        handler.DealErr(err, reply)
    } else {
        resultValueList := funVal.Call([]reflect.Value{reflect.ValueOf(reply)})
        if len(resultValueList) > 0 {
            errInterface := resultValueList[0].Interface()
            if err, ok := errInterface.(error); ok {
                // 如果返回的错误对错误进行处理
                handler.DealErr(err, reply)
            }
        }
    }
    this.response(w, reply)
}

func (this *Http) response(w http.ResponseWriter, reply *HttpReply) {
    switch reply.ReplyType {
    case ReplyTypeHTML:
        this.Conf.HTMLRender.Instance(reply.TemplateName, reply.Data).Render(w, reply.HeaderCode)
    case ReplyTypeJSON:
        this.Conf.JsonRender.Instance(reply.Data).Render(w, reply.HeaderCode)
    }
}

func NewHttp() *Http {
    if onlyHttp == nil {
        onlyHttp = new(Http)
    }
    return onlyHttp
}

